home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / sched / schedule.c < prev   
Encoding:
C/C++ Source or Header  |  1992-12-19  |  37.1 KB  |  1,442 lines

  1. /* 
  2.  * schedule.c --
  3.  *
  4.  *      Routines to implement the fair share scheduler algorithm.
  5.  *
  6.  * Copyright 1986 Regents of the University of California
  7.  * All rights reserved.
  8.  */
  9.  
  10. #ifndef lint
  11. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/sched/schedule.c,v 9.15 92/12/13 18:22:34 mgbaker Exp $ SPRITE (Berkeley)";
  12. #endif /* not lint */
  13.  
  14. #include <sprite.h>
  15. #include <sched.h>
  16. #include <schedInt.h>
  17. #include <proc.h>
  18. #include <list.h>
  19. #include <timer.h>
  20. #include <sync.h>
  21. #include <sys.h>
  22. #include <dbg.h>
  23. #include <mach.h>
  24. #include <bstring.h>
  25. #include <stdio.h>
  26.  
  27. #ifdef spur
  28. #include <devCC.h>
  29. #endif
  30.  
  31. #ifdef sequent
  32. #include "machSGSProc.h"
  33. #include "devClockArbiter.h"    /* for blinky lights */
  34. #endif /* sequent */
  35.  
  36. static int    foundOnDeck[MACH_MAX_NUM_PROCESSORS];
  37. static int    foundInQueue[MACH_MAX_NUM_PROCESSORS];
  38. static int    missedStack[MACH_MAX_NUM_PROCESSORS];
  39.  
  40. /*
  41.  *  The basic philosophy is that processes that have not executed
  42.  *  as much as other processes deserve to be run first.  Thus we
  43.  *  keep a smoothed average of recent CPU usage (the more recent the
  44.  *  usage, the higher the weighting).  The process with the lowest
  45.  *  recent usage gets highest scheduling priority.  The smoothed
  46.  *  average is maintained by adding CPU usage as the process accumulates
  47.  *  it, then periodically (once a second) reducing all the usages of
  48.  *  all processes by a specific factor.  Thus, if a process stops using
  49.  *  the CPU then its average will gradually decay to zero;  if a process
  50.  *  becomes CPU-intensive, its average will gradually increase, up to
  51.  *  a maximum value.  The controlling parameters are:
  52.  *
  53.  *  FORGET_INTERVAL -    How often to reduce everyone's usage.
  54.  *  FORGET_MULTIPLY -
  55.  *  FORGET_SHIFT -    These two factors determine how CPU usage decays:
  56.  *            every second, everyone's CPU usage is multiplied
  57.  *            by FORGET_MULTIPLY, then shifted right by
  58.  *            FORGET_SHIFT.  Right now, the combined effect of
  59.  *            these two is to "forget" 1/8th of the process's
  60.  *            usage.
  61.  */
  62.  
  63. #define FORGET_MULTIPLY        14
  64. #define FORGET_SHIFT        4
  65. #define FORGET_INTERVAL        timer_IntOneSecond
  66.  
  67. /*  
  68.  *  The half-life of the average in seconds can be computed using this formula:
  69.  *
  70.  *        half-life  = ln(2) / ln(F)
  71.  *
  72.  *  where F = (FORGET_MULTIPLY)/(2**FORGET_SHIFT).  For the current settings
  73.  *  the half-life is about 5.1 seconds.  This means that if a process
  74.  *  suddenly stops executing, its usage will decay to half its early value
  75.  *  in about 5 seconds.  The half-life gives an idea of how responsive the
  76.  *  scheduler is to changes in process behavior.  If it responds too slowly,
  77.  *  then a previously-idle process could become CPU-bound and monopolize the
  78.  *  whole CPU for a long time until its usage rises.  If the half-life is
  79.  *  too short, then an interactive process that does anything substantial
  80.  *  (e.g. dragging a selection) will instantly lose its scheduling priority
  81.  *  relative to other compute-bound processes.
  82.  */
  83.  
  84. /*
  85.  * The scheduler module mutex semaphore.  Used in sync module as well,
  86.  * since synchronization involves mucking with the process queues.
  87.  */
  88. Sync_Semaphore sched_Mutex ; 
  89. Sync_Semaphore *sched_MutexPtr = &sched_Mutex;
  90.  
  91. /*
  92.  * Flag to see if Sched_Init has been called.  Used by Sched_GatherProcessInfo
  93.  * to know when things have been initialized. It's needed because GPI
  94.  * is called from the timer module and possibly before Sched_Init has been
  95.  * called.
  96.  */
  97. static Boolean init = FALSE;
  98.  
  99. /*
  100.  * Global variable for the timer queue for Sched_ForgetUsage.
  101.  */
  102. static Timer_QueueElement forgetUsageElement;
  103.  
  104. /*
  105.  * Structure for instrumentation.
  106.  */
  107. Sched_Instrument sched_Instrument;
  108.  
  109. /*
  110.  * Status of each processor.
  111.  */
  112. Sched_ProcessorStatus    sched_ProcessorStatus[MACH_MAX_NUM_PROCESSORS];
  113.  
  114. /*
  115.  * Length of time that a process can run before it is preempted.  This is
  116.  * expressed as a number of timer interrupts.  The quantum length and
  117.  * timer interrupt interval may not divide evenly.
  118.  */
  119.  
  120. int    sched_Quantum = SCHED_DESIRED_QUANTUM / TIMER_CALLBACK_INTERVAL_APPROX;
  121.  
  122.  
  123. Sched_OnDeck    sched_OnDeck[MACH_MAX_NUM_PROCESSORS];
  124.  
  125. /*
  126.  * Forward Declarations.
  127.  */
  128. static void RememberUsage _ARGS_((Proc_ControlBlock *curProcPtr));
  129. static Proc_ControlBlock *IdleLoop _ARGS_((void));
  130. static void QuantumEnd _ARGS_((Proc_ControlBlock *procPtr));
  131. extern void SchedPrintSchedStats _ARGS_((Timer_Ticks time, 
  132.                 ClientData clientData));
  133.  
  134.  
  135. /*
  136.  * ----------------------------------------------------------------------------
  137.  *
  138.  * Sched_Init --
  139.  *
  140.  *      Initialize data structures and variables for the scheduler.
  141.  *    Cause Sched_ForgetUsage to be called from timer callback queue.
  142.  *
  143.  * Results:
  144.  *      None.
  145.  *
  146.  * Side effects:
  147.  *      Global variables are initialized.  Run queue is initialized.
  148.  *
  149.  * ----------------------------------------------------------------------------
  150.  */
  151.  
  152. void
  153. Sched_Init()
  154. {
  155.     int    cpu;
  156.  
  157.     sched_ProcessorStatus[0] = SCHED_PROCESSOR_ACTIVE;
  158.     for(cpu = 0; cpu < MACH_MAX_NUM_PROCESSORS; cpu++) {
  159.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_NOT_STARTED;
  160.     sched_OnDeck[cpu].procPtr = (Proc_ControlBlock *) NIL;
  161.     }
  162.     bzero((Address) &(sched_Instrument),sizeof(sched_Instrument));
  163.  
  164.     List_Init(schedReadyQueueHdrPtr);
  165.     Sync_SemInitDynamic(sched_MutexPtr, "sched_Mutex");
  166.     Sync_SemRegister(sched_MutexPtr);
  167.  
  168.     forgetUsageElement.routine        = Sched_ForgetUsage; 
  169.     forgetUsageElement.clientData    = 0;
  170.     forgetUsageElement.interval        = FORGET_INTERVAL;
  171.     Timer_ScheduleRoutine(&forgetUsageElement, TRUE);
  172.  
  173.     init = TRUE;
  174. }
  175.  
  176. /*
  177.  *----------------------------------------------------------------------
  178.  *
  179.  * Sched_ForgetUsage --
  180.  *
  181.  *    Adjusts the priority for all user processes on the system.
  182.  *  
  183.  *    This routine is called at regular intervals by the 
  184.  *    Timer module TimeOut routine.
  185.  *
  186.  *
  187.  * Results:
  188.  *    none.
  189.  *
  190.  * Side Effects:
  191.  *    Priorities of user processes are modified.
  192.  *
  193.  *----------------------------------------------------------------------
  194.  */
  195.  
  196. /*ARGSUSED*/
  197. void
  198. Sched_ForgetUsage(time, clientData)
  199.     Timer_Ticks time;    /* The absolute time when this routine is called. 
  200.              * (not used). */
  201.     ClientData    clientData;    /* 0 - not used. */
  202. {
  203.     register Proc_ControlBlock *procPtr;
  204.     register int i;
  205.  
  206.     /*
  207.      *  Gain exclusive access to usage fields in the process table.
  208.      */
  209.      MASTER_LOCK(sched_MutexPtr);
  210.  
  211.     /*
  212.      *  Loop through all the processes on the system and
  213.      *  forget some of the CPU usage for them.
  214.      */
  215.     for (i = 0; i < proc_MaxNumProcesses; i++) {
  216.     procPtr = proc_PCBTable[i];
  217.     if (procPtr->state == PROC_UNUSED) {
  218.         continue;
  219.     }
  220.         procPtr->unweightedUsage = 
  221.         (procPtr->unweightedUsage * FORGET_MULTIPLY) >> FORGET_SHIFT;
  222.  
  223.     procPtr->weightedUsage =
  224.         (procPtr->weightedUsage * FORGET_MULTIPLY) >> FORGET_SHIFT;
  225.     }
  226.  
  227.     /*
  228.      *  Schedule this procedure to be called again later.
  229.      */
  230.     Timer_ScheduleRoutine(&forgetUsageElement, TRUE);
  231.  
  232.     MASTER_UNLOCK(sched_MutexPtr);
  233. }
  234.  
  235.  
  236. /*
  237.  *----------------------------------------------------------------------
  238.  *
  239.  *  Sched_GatherProcessInfo --
  240.  *
  241.  *    This routine is called at every timer interrupt. It collects
  242.  *    statistics about the running process such as the state of CPU and
  243.  *    CPU usage. 
  244.  *
  245.  *  Results:
  246.  *    None.
  247.  *
  248.  *  Side Effects:
  249.  *    Various statistics about the running process are collected in the
  250.  *      process's control block.
  251.  *
  252.  *
  253.  *----------------------------------------------------------------------
  254.  */
  255. void
  256. Sched_GatherProcessInfo(interval)
  257.     unsigned int interval;    /* Number of ticks since last invocation. */
  258. {
  259.     register Proc_ControlBlock  *curProcPtr;
  260.     register int        cpu;
  261.  
  262.     if (!init) {
  263.     return;
  264.     }
  265.  
  266.     MASTER_LOCK(sched_MutexPtr);
  267.  
  268.     /*
  269.      *  Get a pointer to the current process from the array that keeps
  270.      *  track of running processes on each processor.
  271.      */
  272.     for (cpu = 0; cpu < mach_NumProcessors; cpu++) {
  273.  
  274.     curProcPtr = proc_RunningProcesses[cpu];
  275.  
  276.     /*
  277.      * If no process is currently running on this processor, don't
  278.      * charge the usage to a particular process but keep track of it.
  279.      */
  280.     if (curProcPtr == (Proc_ControlBlock *) NIL) {
  281.         Timer_AddIntervalToTicks(
  282.             sched_Instrument.processor[cpu].noProcessRunning, 
  283.             interval,
  284.             &(sched_Instrument.processor[cpu].noProcessRunning));
  285.         continue;
  286.     }
  287.  
  288.     /*
  289.      *  We want to gather statistics about how much CPU time is spent in
  290.      *  kernel and user states.  The processor state is determined by
  291.      *  calling a machine-dependent routine.
  292.      */
  293.     if (Mach_ProcessorState(cpu) == MACH_KERNEL) {
  294.         Timer_AddIntervalToTicks(curProcPtr->kernelCpuUsage.ticks, interval,
  295.                    &(curProcPtr->kernelCpuUsage.ticks));
  296.     } else {
  297.         Timer_AddIntervalToTicks(curProcPtr->userCpuUsage.ticks, interval,
  298.                    &(curProcPtr->userCpuUsage.ticks));
  299.     }
  300.  
  301.     /*
  302.      *  Update the CPU usage for scheduling priority calculations
  303.      *  for the current process.
  304.      */
  305.     curProcPtr->recentUsage += interval;
  306.  
  307.     /*
  308.      * See if the quantum has expired for the process.  It can go
  309.      * negative if the user process happened to be running in kernel mode
  310.      * when the quantum expired for the first time and the process has
  311.      * not reentered the kernel voluntarily.
  312.      */
  313.     if ((curProcPtr->genFlags & PROC_USER) && 
  314.         (curProcPtr->billingRate != PROC_NO_INTR_PRIORITY)) {
  315.         if (curProcPtr->schedQuantumTicks != 0) {
  316.         curProcPtr->schedQuantumTicks--;
  317.         }
  318.         if (curProcPtr->schedQuantumTicks == 0) {
  319.         QuantumEnd(curProcPtr);
  320.         }
  321.     }
  322.     }
  323.  
  324.     MASTER_UNLOCK(sched_MutexPtr);
  325. }
  326.  
  327.  
  328. /*
  329.  * ----------------------------------------------------------------------------
  330.  *
  331.  * Sched_ContextSwitchInt --
  332.  *
  333.  *    Change to a new process.  Set the state of the current process
  334.  *    to the state argument.
  335.  *
  336.  *    If no process is runnable, then loop with interrupts enabled and
  337.  *    the master lock released until one is found.
  338.  *
  339.  *    The master lock is assumed to be held with sched_Mutex when
  340.  *    this routine is called.
  341.  *
  342.  * Results:
  343.  *    None.
  344.  *
  345.  * Side effects:
  346.  *    A new process is made runnable.  Counters of context switches are
  347.  *    incremented.
  348.  *
  349.  * ----------------------------------------------------------------------------
  350.  */
  351.  
  352. void
  353. Sched_ContextSwitchInt(state)
  354.     register    Proc_State state;    /* New state of current process */
  355. {
  356.     register Proc_ControlBlock    *curProcPtr;      /* PCB for currently runnning 
  357.                          * process. */
  358.     register Proc_ControlBlock    *newProcPtr;      /* PCB for new process. */
  359.     Proc_ControlBlock        *tnewProcPtr;
  360.     register int cpu;
  361.  
  362.     cpu = Mach_GetProcessorNumber();
  363.     sched_Instrument.processor[cpu].numContextSwitches++;
  364.  
  365.     curProcPtr = Proc_GetCurrentProc();
  366.     /*
  367.      * If we have a context switch pending get rid of it.
  368.      */
  369.     curProcPtr->schedFlags &= ~SCHED_CONTEXT_SWITCH_PENDING;
  370.  
  371.     /*
  372.      * Adjust scheduling priorities.
  373.      */
  374.     RememberUsage(curProcPtr);
  375.     if (state == PROC_READY) {
  376.     /*
  377.      * If the current process is PROC_READY, add it to the ready queue and
  378.      * get the next runnable process.  If that happens to be the current
  379.      */
  380.     curProcPtr->numQuantumEnds++; 
  381.     if (List_IsEmpty(schedReadyQueueHdrPtr)) {
  382.         curProcPtr->schedQuantumTicks = sched_Quantum;
  383.         return;
  384.     }
  385.  
  386.     curProcPtr->state = PROC_READY;
  387.     Sched_InsertInQueue(curProcPtr, &tnewProcPtr);
  388.     newProcPtr = tnewProcPtr;
  389.     if (newProcPtr == (Proc_ControlBlock *) NIL) {
  390.         newProcPtr = IdleLoop();
  391.     } else if (newProcPtr == curProcPtr) {
  392.         curProcPtr->schedQuantumTicks = sched_Quantum;
  393.         curProcPtr->state = PROC_RUNNING;
  394.         return;
  395.     } 
  396.     /*
  397.       * Don't run this process if another processor is already using
  398.      * its stack.
  399.      */
  400.     if (newProcPtr->schedFlags & SCHED_STACK_IN_USE) {
  401.         Sched_InsertInQueue(newProcPtr, (Proc_ControlBlock **) NIL);
  402.         newProcPtr = IdleLoop();
  403.     } 
  404.     } else {
  405.     if (state == PROC_WAITING) {
  406.         curProcPtr->numWaitEvents++; 
  407.     }
  408.     curProcPtr->state = state;
  409.     /*
  410.      * Drop into the idle loop and come out with a runnable process.
  411.      * This procedure exists to try and capture idle time when profiling.
  412.      */
  413.     newProcPtr = IdleLoop();
  414.     }
  415.  
  416.     /*
  417.      * Set the state of the new process.  
  418.      */
  419.     newProcPtr->state = PROC_RUNNING;
  420.     newProcPtr->processor = cpu;
  421. #ifdef sun4
  422.     /*
  423.      * HACK.  The window overflow handler in the sparc mach module spills
  424.      * windows via the CurrentProc pointer when the user's stack is
  425.      * not resident. Before changing the CurrentProc pointer besure that
  426.      * no user windows are active in the register windows.  We need do
  427.      * this only if CurrentProc is changing.  The mach module should
  428.      * be fixed not to use CurrentProc anyway.
  429.      */
  430.      if (newProcPtr != curProcPtr) {
  431.      /*
  432.       * This is overkill because we only need flush the user's windows
  433.       * and not all (kernel and user) windows. It not real bad because
  434.       * we are about to do a Mach_ContextSwitch() which spills all
  435.       * windows anyway.
  436.       */
  437.     Mach_FlushWindowsToStack();
  438.     }
  439. #endif /* sun4 */
  440.     Proc_SetCurrentProc(newProcPtr);
  441.  
  442.     /*
  443.      * Set up the quantum as the number of clocks ticks that this process 
  444.      * is allowed to run berfore it is context-switched.
  445.      * (This field is ignored for kernel processes and user processes with 
  446.      * a billing rate of PROC_NO_INTR_PRIORITY, which allows them to run 
  447.      * forever.)
  448.      */
  449.     newProcPtr->schedQuantumTicks = sched_Quantum;
  450.  
  451.     /*
  452.      * If the current process is continuing, then don't bother to 
  453.      * to do full context switch.  
  454.      */
  455.     if (newProcPtr == curProcPtr) { 
  456.     return;
  457.     }
  458.  
  459.     sched_Instrument.processor[cpu].numFullCS++;
  460.  
  461.     /*
  462.      * Perform the hardware context switch.  After switching, make
  463.      * sure that there is a context for this process.
  464.      */
  465.     newProcPtr->schedFlags |= SCHED_STACK_IN_USE;
  466.     curProcPtr->schedFlags &= ~SCHED_STACK_IN_USE;
  467.     Mach_ContextSwitch(curProcPtr, newProcPtr);
  468. }
  469.  
  470.  
  471. /*
  472.  *----------------------------------------------------------------------
  473.  *
  474.  * RememberUsage --
  475.  *
  476.  *    Adjusts the weighted and unweighted CPU usages for a kernel or
  477.  *    and user process. A process with the billingRate of 
  478.  *    PROC_NO_INTR_PRIORITY does not get charged for weighted CPU usage,
  479.  *    which is used in deciding priority in the run queue.
  480.  *    
  481.  *    This routine assumes the sched_Mutex master lock is held.
  482.  *
  483.  * Results:
  484.  *    None.
  485.  *
  486.  * Side Effects:
  487.  *    CPU usages of the process are modified.
  488.  *
  489.  *----------------------------------------------------------------------
  490.  */
  491.  
  492. static void
  493. RememberUsage(curProcPtr)
  494.     register Proc_ControlBlock *curProcPtr;    /* The process that will be 
  495.                          * adjusted */
  496. {
  497.     register int billingRate = curProcPtr->billingRate;
  498.  
  499.     /*
  500.      *  We want to calculate the process's CPU usage at this moment.
  501.      *  There are 2 smoothed usage averages that we maintain: an
  502.      *  unweighted value and a weighted value.  The weighted usage is used
  503.      *  for calculating scheduling priority.  The unweighted usage keeps
  504.      *  track of the real smoothed usage.
  505.      */ 
  506.  
  507.     curProcPtr->unweightedUsage += curProcPtr->recentUsage;
  508.  
  509.     /*
  510.      *  The billing rate basically specifies a process's scheduling 
  511.      *  priority. It it used to modify the amount of the recent usage
  512.      *  that gets added to the weighted usage.
  513.      *
  514.      *  If the billing rate equals the normal value then the recent usage
  515.      *  is not multiplied or divided by any factor.  If the billing rate
  516.      *  is greater than the normal value then only a faction of the recent
  517.      *  usage is added to the weighted usage.  If the billing rate is less
  518.      *  than the normal value then the recent usage is multiplied by a
  519.      *  power of 2 before it is added to the weighted  usage.
  520.      *
  521.      *  A process with a billing rate of PROC_NO_INTR_PRIORITY does
  522.      *  not get charged for CPU usage.
  523.      */
  524.  
  525.  
  526.     if (billingRate >= PROC_NORMAL_PRIORITY) {
  527.     if (billingRate != PROC_NO_INTR_PRIORITY) {
  528.         curProcPtr->weightedUsage += curProcPtr->recentUsage >> billingRate;
  529.     }
  530.     } else {
  531.     curProcPtr->weightedUsage += curProcPtr->recentUsage << -(billingRate);
  532.     }
  533.  
  534.     /*
  535.      *  Reset the recent usage back to zero.
  536.      */
  537.  
  538.     curProcPtr->recentUsage = 0;
  539. }
  540.  
  541. /*
  542.  *----------------------------------------------------------------------
  543.  *
  544.  * IdleLoop --
  545.  *
  546.  *    This fetches a runnable process from the ready queue and returns it.
  547.  *    If none are available this goes into an idle loop, enabling and
  548.  *    disabling interrupts, and waits for something to become runnable.
  549.  *
  550.  * Results:
  551.  *    A pointer to the next process to run.
  552.  *
  553.  * Side effects:
  554.  *    Momentarily enables interrupts.
  555.  *
  556.  *----------------------------------------------------------------------
  557.  */
  558.  
  559. static Proc_ControlBlock *
  560. IdleLoop()
  561. {
  562.     register Proc_ControlBlock    *procPtr;
  563.     register int cpu;
  564.     register List_Links        *queuePtr;
  565.     register Boolean        foundOne;
  566.     Proc_ControlBlock        *lastProcPtr = Proc_GetCurrentProc();
  567.     Boolean            onReadyQueue;
  568. #ifdef spur 
  569.     /* Turn off perf counters. */
  570.     Dev_CCSetCounters(COUNTERS_OFF);
  571. #endif
  572.  
  573.     cpu = Mach_GetProcessorNumber();
  574.     queuePtr = schedReadyQueueHdrPtr;
  575.     if (sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_ACTIVE) {
  576.     foundOne = FALSE;
  577.     procPtr = (Proc_ControlBlock *) List_First(queuePtr);
  578.     while (!List_IsAtEnd(queuePtr,(List_Links *) procPtr)) {
  579.         if (!(procPtr->schedFlags & SCHED_STACK_IN_USE) ||
  580.          (procPtr->processor == cpu)) {
  581.         foundOne = TRUE;
  582.         break; 
  583.         }
  584.         if (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  585.         missedStack[cpu]++;
  586.         }
  587.         procPtr = (Proc_ControlBlock *)List_Next((List_Links *)procPtr);
  588.     }
  589.     if (foundOne) {
  590.         /*
  591.          * We found a READY process for us, break out of the
  592.          * idle loop.
  593.          */
  594.         onReadyQueue = TRUE;
  595.         foundInQueue[cpu]++;
  596. #ifdef spur
  597.         Mach_InstCountOff(0);
  598. #endif
  599.         goto exit;
  600.     }
  601.     }
  602. #ifdef sun4
  603.     /*
  604.      * HACK.  The window overflow handler in the sparc mach module spills
  605.      * windows via the CurrentProc pointer when the user's stack is
  606.      * not resident.  Before nuking the CurrentProc point besure
  607.      * that no user window is resident. THIS SHOULD BE FIXED.
  608.      */
  609.     Mach_FlushWindowsToStack();
  610. #endif /* sun4 */
  611.     Proc_SetCurrentProc((Proc_ControlBlock *) NIL);
  612. #ifdef spur
  613.     Mach_InstCountEnd(0);
  614. #endif
  615.     MASTER_UNLOCK(sched_MutexPtr);
  616.  
  617.     if (Mach_AtInterruptLevel()) {
  618.     Mach_EnableIntr();
  619.     panic("At interrupt level going into idle loop!\n");
  620.     }
  621.     if (Mach_IntrNesting(cpu) != 0) {
  622.     int i;
  623.  
  624.     Mach_EnableIntr();
  625.     i = Mach_IntrNesting(cpu);
  626.     mach_NumDisableIntrsPtr[cpu] = 0;
  627.     Mach_EnableIntr();
  628.     panic("Interrupt level at %d going into idle loop.\n", i);
  629.     }
  630.  
  631. #ifdef sequent
  632.     /*
  633.      * Really going idle, turn off the front panel light
  634.      * and the processor board light.
  635.      */
  636.     if (light_show) {
  637.     if (fp_lights) {
  638.         FP_LIGHTOFF(cpu);
  639.     }
  640.     *(int *)PHYS_LED = 0;
  641.     }
  642. #endif /* sequent */
  643.  
  644.     while (1) {
  645.     /*
  646.      * Wait for a process to become runnable.  
  647.      */
  648.     if (((List_IsEmpty(queuePtr) == FALSE) ||
  649.          (sched_OnDeck[cpu].procPtr != (Proc_ControlBlock *) NIL)) &&
  650.         ((sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_ACTIVE) ||
  651.          (sched_ProcessorStatus[cpu] == SCHED_PROCESSOR_COUNTING_TICKS) ||
  652.          (lastProcPtr->state == PROC_READY))) {
  653.         /*
  654.          * Looks like there might be something in the queue. We don't
  655.          * have sched_Mutex down at this point, so this is only a hint.
  656.          */
  657.         MASTER_LOCK(sched_MutexPtr);
  658. #ifdef spur
  659.         Mach_InstCountStart(2);
  660. #endif
  661.         /*
  662.          * Look and see if there is anything for us on deck.
  663.          */
  664.         procPtr = sched_OnDeck[cpu].procPtr;
  665.         if (procPtr != (Proc_ControlBlock *) NIL) {
  666.         if ((procPtr->schedFlags & SCHED_STACK_IN_USE) &&
  667.             (procPtr->processor != cpu)) {
  668.             panic("Process with stack in use in the staging area.");
  669.         }
  670.         sched_OnDeck[cpu].procPtr = (Proc_ControlBlock *) NIL;
  671.         onReadyQueue = FALSE;
  672.         foundOnDeck[cpu]++;
  673. #ifdef spur
  674.         Mach_InstCountOff(2);
  675. #endif
  676.         break;
  677.         }
  678.         /*
  679.          * If we are counting ticks then we are waiting for one 
  680.          * specific process to wake up, and it will show up in the
  681.          * staging area.  If we didn't find one there then skip to
  682.          * the bottom of the loop.
  683.          */
  684.         if (sched_ProcessorStatus[cpu] != SCHED_PROCESSOR_COUNTING_TICKS) {
  685.         /*
  686.          * Make sure queue is not empty. If there is a ready process
  687.          * take a peek at it to insure that we can execute it. The
  688.          * only condition preventing a processor from executing a
  689.          * process is that its stack is being used by another processor.
  690.          */
  691.         foundOne = FALSE;
  692.         procPtr = (Proc_ControlBlock *) List_First(queuePtr);
  693.         while (!List_IsAtEnd(queuePtr,(List_Links *) procPtr)) {
  694.             if (!(procPtr->schedFlags & SCHED_STACK_IN_USE) ||
  695.              (procPtr->processor == cpu)) {
  696.             foundOne = TRUE;
  697.             break; 
  698.             }
  699.             if (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  700.             missedStack[cpu]++;
  701.             }
  702.             procPtr = (Proc_ControlBlock *)
  703.             List_Next((List_Links *)procPtr);
  704.         }
  705.         if (foundOne) {
  706.             /*
  707.              * We found a READY processor for us, break out of the
  708.              * idle loop.
  709.              */
  710.              onReadyQueue = TRUE;
  711.              foundInQueue[cpu]++;
  712. #ifdef spur
  713.             Mach_InstCountOff(2);
  714. #endif
  715.             break;
  716.         }
  717.         }
  718.         sync_InstrumentPtr[cpu]->sched_MutexMiss++;
  719. #ifdef spur
  720.         Mach_InstCountEnd(2);
  721. #endif
  722.         MASTER_UNLOCK(sched_MutexPtr);
  723.     }
  724.     /*
  725.      * Count Idle ticks.  
  726.      */
  727.     if (sched_Instrument.processor[cpu].idleTicksLow ==
  728.                     (unsigned) 0xffffffff) {
  729.         sched_Instrument.processor[cpu].idleTicksLow = 0;
  730.         sched_Instrument.processor[cpu].idleTicksOverflow++;
  731.     } else {
  732.         sched_Instrument.processor[cpu].idleTicksLow++;
  733.     }
  734.     }
  735. exit:
  736. #ifdef spur
  737.     Mach_InstCountStart(1);
  738. #endif
  739. #ifdef spur
  740.     Dev_CCSetCounters(COUNTERS_RESTORE); /* Restore perf counters. */
  741. #endif
  742.  
  743.     if (procPtr->state != PROC_READY) {
  744.     /*
  745.      * Unlock sched_Mutex because panic tries to grab it somewhere.
  746.      * Do the panic by hand, without syncing the disks, because
  747.      * we still deadlock someplace.
  748.      */
  749.     MASTER_UNLOCK(sched_MutexPtr);
  750.     printf("Fatal Error: Non-ready process found in ready queue.\n");
  751.     DBG_CALL;
  752.     MASTER_LOCK(sched_MutexPtr);
  753.     }
  754.     if (onReadyQueue == TRUE) {
  755.     ((List_Links *)procPtr)->prevPtr->nextPtr =
  756.                         ((List_Links *)procPtr)->nextPtr;
  757.     ((List_Links *)procPtr)->nextPtr->prevPtr =
  758.                         ((List_Links *)procPtr)->prevPtr;
  759.     /*
  760.     List_Remove((List_Links *)procPtr);
  761.     */
  762.     sched_Instrument.numReadyProcesses -= 1;
  763.     }
  764.  
  765. #ifdef sequent
  766.     /*
  767.      * Leaving idle, turn on the front panel light
  768.      * and the processor board light.
  769.      */
  770.     if (light_show) {
  771.     if (fp_lights) {
  772.         FP_LIGHTON(cpu);
  773.     }
  774.     *(int *)PHYS_LED = 1;
  775.     }
  776. #endif /* sequent */
  777.  
  778.     return(procPtr);
  779. }
  780.  
  781. /*
  782.  *----------------------------------------------------------------------
  783.  *
  784.  * Sched_TimeTicks --
  785.  *
  786.  *    Idle for a few seconds and count the ticks recorded in IdleLoop.
  787.  *    For now, we only do this for one processor. All we're trying to get
  788.  *    is a rough estimate of idleTicksPerSecond.
  789.  *
  790.  *      This procedure is called during boot. The results are pretty much
  791.  *    meaningless if it is not.
  792.  *    
  793.  *    For best results all interrupts except for timer interrupts should
  794.  *    be off.  If we are on a multiprocessor then we idle all processors
  795.  *    for first so they don't interfere as badly.  Interrupts will still
  796.  *    screw us up (they are handled by processor 1 but they still use
  797.  *    locks we may need),  but I don't think turning off interrupts
  798.  *    for 5 seconds on a live system is a good idea.
  799.  *
  800.  * Results:
  801.  *    None.
  802.  *
  803.  * Side effects:
  804.  *    Momentarily enables interrupts.
  805.  *
  806.  *----------------------------------------------------------------------
  807.  */
  808.  
  809. void
  810. Sched_TimeTicks()
  811. {
  812.     register int lowTicks;
  813.     register int cpu;
  814.     Time time;
  815.     int i;
  816.     Boolean    wasIdled[MACH_MAX_NUM_PROCESSORS];
  817.  
  818.     cpu = Mach_GetProcessorNumber(); 
  819.     if (cpu != 0) {
  820.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_COUNTING_TICKS;
  821.     for (i = 0; i < mach_NumProcessors; i++) {
  822.          if (sched_ProcessorStatus[i] == SCHED_PROCESSOR_ACTIVE) {
  823.          (void) Sched_IdleProcessor(i);
  824.          wasIdled[i] = TRUE;
  825.          } else {
  826.          wasIdled[i] = FALSE;
  827.          }
  828.      }
  829.     }
  830.     Time_Multiply(time_OneSecond, 5, &time);
  831.     printf("Idling processor %d for 5 seconds...",cpu);
  832.     lowTicks = sched_Instrument.processor[cpu].idleTicksLow;
  833.     (void) Sync_WaitTime(time);
  834.     lowTicks = sched_Instrument.processor[cpu].idleTicksLow - lowTicks;
  835.     printf(" %d ticks\n", lowTicks);
  836.     sched_Instrument.processor[cpu].idleTicksPerSecond = lowTicks / 5;
  837.     sched_ProcessorStatus[cpu] = SCHED_PROCESSOR_ACTIVE;
  838.     if (cpu != 0) {
  839.     for (i = 0; i < mach_NumProcessors; i++) {
  840.          if (wasIdled[i]) {
  841.          (void) Sched_StartProcessor(i);
  842.          }
  843.      }
  844.      }
  845. }
  846.  
  847.  
  848. /*
  849.  *----------------------------------------------------------------------
  850.  *
  851.  * QuantumEnd --
  852.  *
  853.  *    Called by Sched_GatherProcessInfo when a process's quantum has expired.
  854.  *    A global flag is set to indicate that the current process should
  855.  *    be involuntarily context switched at the next available moment.
  856.  *    If the process is executing in kernel mode, then don't force a context
  857.  *    switch now, but instead mark the process as having a context switch
  858.  *    pending.
  859.  *
  860.  *    N.B. This routine assumes the sched mutex is already locked.
  861.  *
  862.  * Results:
  863.  *    None.
  864.  *
  865.  * Side effects:
  866.  *    A context switch is initiated.
  867.  *
  868.  *----------------------------------------------------------------------
  869.  */
  870.  
  871. static void
  872. QuantumEnd(procPtr)
  873.     register    Proc_ControlBlock     *procPtr;
  874. {
  875.     procPtr->schedFlags |= SCHED_CONTEXT_SWITCH_PENDING;
  876.     procPtr->specialHandling = 1;
  877.     if (procPtr->processor != Mach_GetProcessorNumber()) {
  878.     /* 
  879.      * If the process whose quantum has ended is running on a different
  880.      * processor we need to poke the processor and force it into the
  881.      * kernel. On its way back to user mode the special handling flag
  882.      * will be checked and a context switch will occur (assuming that
  883.      * the offending process is still running).
  884.      */
  885.     Mach_CheckSpecialHandling(procPtr->processor);
  886.     }
  887. }
  888.  
  889. /*
  890.  *----------------------------------------------------------------------
  891.  *
  892.  * Sched_PrintStat --
  893.  *
  894.  *    Print the sched module statistics.
  895.  *
  896.  * Results:
  897.  *    None.
  898.  *
  899.  * Side effects:
  900.  *    Do the prints.
  901.  *
  902.  *----------------------------------------------------------------------
  903.  */
  904. void
  905. Sched_PrintStat()
  906. {
  907.     Time  tmp;
  908.     int   i;
  909.  
  910.     printf("Sched Statistics\n");
  911.     for(i = 0; i < mach_NumProcessors;i++) {
  912.     printf("Processor: %d\n",i);
  913.     printf("numContextSwitches = %d\n",
  914.            sched_Instrument.processor[i].numContextSwitches);
  915.     printf("numFullSwitches    = %d\n",
  916.            sched_Instrument.processor[i].numFullCS);
  917.     printf("numInvoluntary     = %d\n",
  918.            sched_Instrument.processor[i].numInvoluntarySwitches);
  919.     Timer_TicksToTime(sched_Instrument.processor[i].noProcessRunning, &tmp);
  920.     printf("Idle Time          = %d.%06d seconds\n", 
  921.              tmp.seconds, tmp.microseconds);
  922.     }
  923. }
  924.  
  925.  
  926. /*
  927.  *----------------------------------------------------------------------
  928.  *
  929.  * Sched_LockAndSwitch --
  930.  *
  931.  *    Acquires the Master Lock and performs a context switch.
  932.  *    Called when a process's quantum has expired and a trace trap
  933.  *    exception has arisen with the sched_ContextSwitchInProgress flag set.
  934.  *
  935.  * Results:
  936.  *    None.
  937.  *
  938.  * Side effects:
  939.  *    A context switch is performed.  The count of involuntary switches is
  940.  *    incremented.
  941.  *
  942.  *----------------------------------------------------------------------
  943.  */
  944.  
  945. void
  946. Sched_LockAndSwitch()
  947. {
  948.     MASTER_LOCK(sched_MutexPtr);
  949.     sched_Instrument.processor[Mach_GetProcessorNumber()].
  950.                 numInvoluntarySwitches++;
  951.     Sched_ContextSwitchInt(PROC_READY);
  952. #ifdef spur
  953.     Mach_InstCountEnd(1);
  954. #endif
  955.  
  956.     MASTER_UNLOCK(sched_MutexPtr);
  957. }
  958.  
  959.  
  960. /*
  961.  *----------------------------------------------------------------------
  962.  *
  963.  * Sched_ContextSwitch --
  964.  *
  965.  *    Acquires the Master Lock and performs a context switch.
  966.  *
  967.  * Results:
  968.  *    None.
  969.  *
  970.  * Side effects:
  971.  *    A context switch is performed.
  972.  *
  973.  *----------------------------------------------------------------------
  974.  */
  975.  
  976. void
  977. Sched_ContextSwitch(state)
  978.     Proc_State    state;
  979. {
  980.  
  981.     MASTER_LOCK(sched_MutexPtr);
  982.     Sched_ContextSwitchInt(state);
  983. #ifdef spur
  984.     Mach_InstCountEnd(1);
  985. #endif
  986.     MASTER_UNLOCK(sched_MutexPtr);
  987.  
  988. }
  989.  
  990.  
  991.  
  992. /*
  993.  *----------------------------------------------------------------------
  994.  *
  995.  * Sched_StartKernProc --
  996.  *
  997.  *    Start a process by unlocking the master lock and calling the
  998.  *    function whose address has been passed to us as an argument.
  999.  *    If the function returns then exit.
  1000.  *
  1001.  *
  1002.  * Results:
  1003.  *    None.
  1004.  *
  1005.  * Side effects:
  1006.  *    The master lock is released.
  1007.  *
  1008.  *----------------------------------------------------------------------
  1009.  */
  1010. void
  1011. Sched_StartKernProc(func)
  1012.     void    (*func)();
  1013. {
  1014. #ifdef spur
  1015.     Mach_InstCountEnd(1);
  1016. #endif
  1017.     MASTER_UNLOCK(sched_MutexPtr);
  1018.     func();
  1019.     Proc_Exit(0);
  1020. }
  1021.  
  1022.  
  1023. /*
  1024.  *----------------------------------------------------------------------
  1025.  *
  1026.  * Sched_MakeReady --
  1027.  *
  1028.  *    Put the process on the ready queue.
  1029.  *
  1030.  * Results:
  1031.  *    None.
  1032.  *
  1033.  * Side effects:
  1034.  *    State of given process changed to ready.
  1035.  *
  1036.  *----------------------------------------------------------------------
  1037.  */
  1038.  
  1039. void
  1040. Sched_MakeReady(procPtr)
  1041.     register    Proc_ControlBlock    *procPtr;
  1042. {
  1043.     MASTER_LOCK(sched_MutexPtr);
  1044.     procPtr->state = PROC_READY;
  1045.     Sched_InsertInQueue(procPtr, (Proc_ControlBlock **) NIL);
  1046.     MASTER_UNLOCK(sched_MutexPtr);
  1047. }
  1048.  
  1049.  
  1050. /*
  1051.  *----------------------------------------------------------------------
  1052.  *
  1053.  * Sched_StartUserProc --
  1054.  *
  1055.  *    Start a user process running.  This is the first thing that is
  1056.  *     called when a newly created process begins execution.
  1057.  *
  1058.  * Results:
  1059.  *    None.
  1060.  *
  1061.  * Side effects:
  1062.  *    None.
  1063.  *
  1064.  *----------------------------------------------------------------------
  1065.  */
  1066.  
  1067. void
  1068. Sched_StartUserProc(pc)
  1069.     Address    pc;    /* Program counter where process is to start
  1070.              * executing. */
  1071. {
  1072.     register         Proc_ControlBlock *procPtr;
  1073.  
  1074. #ifdef spur
  1075.     Mach_InstCountEnd(1);
  1076. #endif
  1077.     MASTER_UNLOCK(sched_MutexPtr);
  1078.     procPtr = Proc_GetCurrentProc();
  1079.  
  1080. #ifdef notdef
  1081.     Proc_Lock(procPtr);
  1082.     procPtr->genFlags |= PROC_DONT_MIGRATE;
  1083.     Proc_Unlock(procPtr);
  1084. #endif
  1085.     
  1086.     /*
  1087.      * Start the process running.  This does not return.
  1088.      */
  1089.     Mach_StartUserProc(procPtr, pc);
  1090. }
  1091. #if (MACH_MAX_NUM_PROCESSORS != 1)
  1092.  
  1093.  
  1094. /*
  1095.  *----------------------------------------------------------------------
  1096.  *
  1097.  * ProcessorStartProcess --
  1098.  *
  1099.  *    The initial process of a processor.
  1100.  *
  1101.  * Results:
  1102.  *    None.
  1103.  *
  1104.  * Side effects:
  1105.  *    
  1106.  *
  1107.  *----------------------------------------------------------------------
  1108.  */
  1109.  
  1110. static 
  1111. void ProcessorStartProcess()
  1112. {
  1113.        /*
  1114.          * Detach from parent so that cleanup will occur when the
  1115.          * processor exits with this process. Also set the SCHED_STACK_IN_USE
  1116.          * flag so that cleanup wont happen too early.
  1117.          */
  1118.         Proc_Detach(SUCCESS);
  1119.         Sched_ContextSwitch(PROC_WAITING);
  1120.     Proc_Exit(0);
  1121. }
  1122.  
  1123.  
  1124.  
  1125. /*
  1126.  *----------------------------------------------------------------------
  1127.  *
  1128.  * StartProcessor --
  1129.  *
  1130.  *    Start up a processor..
  1131.  *
  1132.  * Results:
  1133.  *    None.
  1134.  *
  1135.  * Side effects:
  1136.  *    
  1137.  *
  1138.  *----------------------------------------------------------------------
  1139.  */
  1140.  
  1141. ReturnStatus
  1142. StartProcessor(pnum)
  1143.     int        pnum;        /* Processor number to start. */
  1144. {
  1145.     Proc_PID    pid;
  1146.     Proc_ControlBlock *procPtr;
  1147.     char        procName[128];
  1148.     ReturnStatus status;
  1149.  
  1150.     /*
  1151.      * Startup an initial process for the processor pnum.  
  1152.      */
  1153.     sprintf(procName,"Processor%dStart",pnum);
  1154.     Proc_NewProc((Address)ProcessorStartProcess, PROC_KERNEL, FALSE, &pid,
  1155.                     procName, FALSE);
  1156.     procPtr = Proc_GetPCB(pid);
  1157.     /*
  1158.      * Wait for this processor to go into the WAIT state.
  1159.      */
  1160.     while (procPtr->state != PROC_WAITING) {
  1161.     (void) Sync_WaitTimeInterval(10 * timer_IntOneMillisecond);
  1162.     }
  1163.  
  1164.     /*
  1165.      * Wait for its stack to become free .
  1166.      */
  1167.     while (procPtr->schedFlags & SCHED_STACK_IN_USE) {
  1168.     (void) Sync_WaitTimeInterval(10 * timer_IntOneMillisecond);
  1169.     }
  1170.     Sched_ContextSwitch(PROC_READY);
  1171.     printf("Starting processor %d with pid 0x%x\n",pnum,pid);
  1172.     status = Mach_SpinUpProcessor(pnum,procPtr);
  1173.     if (status != SUCCESS) {
  1174.     printf("Warning: Processor %d not started.\n",pnum);
  1175.     }
  1176.     return (status);
  1177. }
  1178. #endif
  1179.  
  1180. /*
  1181.  *----------------------------------------------------------------------
  1182.  *
  1183.  * Sched_StartProcessor --
  1184.  *
  1185.  *    Start a processor running processes.
  1186.  *
  1187.  * Results:
  1188.  *    A return status.
  1189.  *
  1190.  * Side effects:
  1191.  *    A processor maybe started.
  1192.  *
  1193.  *----------------------------------------------------------------------
  1194.  */
  1195. ReturnStatus
  1196. Sched_StartProcessor(pnum)
  1197.     int        pnum;    /* Processor number to start. */
  1198. {
  1199.     ReturnStatus    status;
  1200.     /*
  1201.      * Insure that processor number is in range.   
  1202.      * 
  1203.      */
  1204.     if (pnum >= MACH_MAX_NUM_PROCESSORS) {
  1205.     return (GEN_INVALID_ARG);
  1206.     }
  1207.     MASTER_LOCK(sched_MutexPtr);
  1208.     switch (sched_ProcessorStatus[pnum]) { 
  1209.     case SCHED_PROCESSOR_IDLE: {
  1210.         sched_ProcessorStatus[pnum] = SCHED_PROCESSOR_ACTIVE;
  1211.         /*
  1212.          * Fall thru .
  1213.          */
  1214.     }
  1215.     case SCHED_PROCESSOR_STARTING:
  1216.     case SCHED_PROCESSOR_ACTIVE: {
  1217.         status = SUCCESS;
  1218.         break;
  1219.     }
  1220.     case SCHED_PROCESSOR_NOT_STARTED: {
  1221. #if (MACH_MAX_NUM_PROCESSORS != 1)
  1222.         sched_ProcessorStatus[pnum] == SCHED_PROCESSOR_STARTING;
  1223.         MASTER_UNLOCK(sched_MutexPtr);
  1224.         status = StartProcessor(pnum);
  1225.         return (status);
  1226. #endif
  1227.     } 
  1228.     default: {
  1229.         printf("Warning: Unknown processor state %d for processor %d\n",
  1230.             (int) sched_ProcessorStatus[pnum], pnum);
  1231.         status = FAILURE;
  1232.     }
  1233.     }
  1234.     MASTER_UNLOCK(sched_MutexPtr);
  1235.     return (status);
  1236. }
  1237.  
  1238.  
  1239.  
  1240. /*
  1241.  *----------------------------------------------------------------------
  1242.  *
  1243.  * Sched_IdleProcessor --
  1244.  *
  1245.  *    Put a processor into the idle state so it wont be scheduled for
  1246.  *    anymore processes.
  1247.  *
  1248.  * Results:
  1249.  *    None.
  1250.  *
  1251.  * Side effects:
  1252.  *    A processor will be idled started.
  1253.  *
  1254.  *----------------------------------------------------------------------
  1255.  */
  1256. ReturnStatus
  1257. Sched_IdleProcessor(pnum)
  1258.     int        pnum;    /* Processor number to start. */
  1259. {
  1260.     ReturnStatus    status;
  1261.     /*
  1262.      * Insure that processor number is in range.   
  1263.      * 
  1264.      */
  1265.  
  1266. #ifdef sequent
  1267.     if ((pnum < 0) || (pnum >= mach_NumProcessors)) {
  1268.         return GEN_INVALID_ARG;
  1269.     }
  1270. #else /* sequent */
  1271.     if (pnum >= MACH_MAX_NUM_PROCESSORS) {
  1272.     return (GEN_INVALID_ARG);
  1273.     }
  1274. #endif /* sequent */
  1275.     MASTER_LOCK(sched_MutexPtr);
  1276.     switch (sched_ProcessorStatus[pnum]) { 
  1277.     case SCHED_PROCESSOR_ACTIVE: 
  1278.         sched_ProcessorStatus[pnum] = SCHED_PROCESSOR_IDLE;
  1279.         /*
  1280.          * Fall thru.
  1281.          */
  1282.     case SCHED_PROCESSOR_IDLE: {
  1283.         status = SUCCESS;
  1284.         break;
  1285.     }
  1286.     case SCHED_PROCESSOR_NOT_STARTED: 
  1287.     case SCHED_PROCESSOR_STARTING: {
  1288.         status = GEN_INVALID_ARG;
  1289.         break;
  1290.     }
  1291.     default: {
  1292.         printf("Warning: Unknown processor state %d for processor %d\n",
  1293.             (int) sched_ProcessorStatus[pnum], pnum);
  1294.         status = FAILURE;
  1295.     }
  1296.     }
  1297.     MASTER_UNLOCK(sched_MutexPtr);
  1298.     return (status);
  1299. }
  1300.  
  1301.  
  1302. /*
  1303.  *----------------------------------------------------------------------
  1304.  *
  1305.  * Sched_DumpReadyQueue --
  1306.  *
  1307.  *    Print out the contents of the ready queue.
  1308.  *
  1309.  * Results:
  1310.  *    None.
  1311.  *
  1312.  * Side effects:
  1313.  *    Output goes to the screen.
  1314.  *
  1315.  *----------------------------------------------------------------------
  1316.  */
  1317.  
  1318. /* ARGSUSED */
  1319. void
  1320. Sched_DumpReadyQueue(dummy)
  1321.     ClientData dummy;
  1322. {
  1323.     List_Links *itemPtr;
  1324.     Proc_ControlBlock *snapshot[SCHED_MAX_DUMP_SIZE];
  1325.     int snapshotCnt;
  1326.     int overflow;
  1327.     int i;
  1328.  
  1329.     if (List_IsEmpty(schedReadyQueueHdrPtr)) {
  1330.     printf("\nReady queue is empty.\n");
  1331.     } else {
  1332.     printf("\n%8s %5s %10s %10s %8s %8s   %s\n",
  1333.         "ID", "wtd", "user", "kernel", "event", "state", "name");
  1334.     overflow = FALSE;
  1335.     snapshotCnt = 0;
  1336.     MASTER_LOCK(sched_MutexPtr);
  1337.     LIST_FORALL(schedReadyQueueHdrPtr,itemPtr) {
  1338.         if (snapshotCnt >= SCHED_MAX_DUMP_SIZE) {
  1339.         overflow = TRUE;
  1340.         break;
  1341.         }
  1342.         snapshot[snapshotCnt++] = (Proc_ControlBlock *) itemPtr;
  1343.     }
  1344.     MASTER_UNLOCK(sched_MutexPtr);
  1345.     for (i = 0; i <snapshotCnt; i++) {
  1346.         Proc_DumpPCB(snapshot[i]);
  1347.     }
  1348.     if (overflow) {
  1349.         printf("Ready queue too large to snapshot.\n");
  1350.     }
  1351.     }
  1352. }
  1353.  
  1354.  
  1355. /*
  1356.  * Temporary call-back for printing sched statistics for recovery.
  1357.  */
  1358. Timer_QueueElement      schedStatElement;
  1359. Boolean                 getSchedStats = FALSE;
  1360.  
  1361. /*ARGSUSED*/
  1362. void
  1363. SchedPrintSchedStats(time, clientData)
  1364.     Timer_Ticks time;
  1365.     ClientData  clientData;
  1366. {
  1367.     int                 i;
  1368.  
  1369.     /* print stuff */
  1370.     Sched_PrintStat();
  1371.     for (i = 0; i < mach_NumProcessors; i++) {
  1372.     printf("processor %d:\n", i);
  1373.         printf("idleTicksLow: %d\n",
  1374.         sched_Instrument.processor[i].idleTicksLow);
  1375.         printf("idleTicksOverflow: %d\n",
  1376.         sched_Instrument.processor[i].idleTicksOverflow);
  1377.     }
  1378.     printf("\n");
  1379.  
  1380.     if (getSchedStats) {
  1381.         Timer_ScheduleRoutine(&schedStatElement, TRUE);
  1382.     }
  1383.     return;
  1384. }
  1385.  
  1386.  
  1387.  
  1388. /*
  1389.  *----------------------------------------------------------------------
  1390.  *
  1391.  * Sched_StartSchedStats --
  1392.  *
  1393.  *      Start up the kernel's periodic printing of sched stats.
  1394.  *      Temporary routine for recovery statistics.
  1395.  *
  1396.  * Results:
  1397.  *      None.
  1398.  *
  1399.  * Side effects:
  1400.  *      Call-back routine scheduled.
  1401.  *
  1402.  *----------------------------------------------------------------------
  1403.  */
  1404. void
  1405. Sched_StartSchedStats()
  1406. {
  1407.     schedStatElement.routine = SchedPrintSchedStats;
  1408.     schedStatElement.clientData = 0;
  1409.     schedStatElement.interval = timer_IntOneSecond * 10;
  1410.     getSchedStats = TRUE;
  1411.     Timer_ScheduleRoutine(&schedStatElement, TRUE);
  1412.  
  1413.     return;
  1414. }
  1415.  
  1416.  
  1417. /*
  1418.  *----------------------------------------------------------------------
  1419.  *
  1420.  * Sched_StopSchedStats --
  1421.  *
  1422.  *      Stop the kernel's periodic printing of sched stats.
  1423.  *      Temporary routine for recovery statistics.
  1424.  *
  1425.  * Results:
  1426.  *      None.
  1427.  *
  1428.  * Side effects:
  1429.  *      Call-back routine descheduled.
  1430.  *
  1431.  *----------------------------------------------------------------------
  1432.  */
  1433. void
  1434. Sched_StopSchedStats()
  1435. {
  1436.     getSchedStats = FALSE;
  1437.     (void) Timer_DescheduleRoutine(&schedStatElement);
  1438.  
  1439.     return;
  1440. }
  1441.  
  1442.